<?php


namespace app\common\logic;


use PaypalServerSdkLib\Authentication\ClientCredentialsAuthCredentialsBuilder;
use PaypalServerSdkLib\Authentication\ClientCredentialsAuthManager;
use PaypalServerSdkLib\Environment;
use PaypalServerSdkLib\Models\Builders\AmountBreakdownBuilder;
use PaypalServerSdkLib\Models\Builders\AmountWithBreakdownBuilder;
use PaypalServerSdkLib\Models\Builders\CardAttributesBuilder;
use PaypalServerSdkLib\Models\Builders\CardRequestBuilder;
use PaypalServerSdkLib\Models\Builders\CardVerificationBuilder;
use PaypalServerSdkLib\Models\Builders\MoneyBuilder;
use PaypalServerSdkLib\Models\Builders\OrderRequestBuilder;
use PaypalServerSdkLib\Models\Builders\PaymentSourceBuilder;
use PaypalServerSdkLib\Models\Builders\PaypalWalletBuilder;
use PaypalServerSdkLib\Models\Builders\PaypalWalletExperienceContextBuilder;
use PaypalServerSdkLib\Models\Builders\PurchaseUnitRequestBuilder;
use PaypalServerSdkLib\Models\CardRequest;
use PaypalServerSdkLib\Models\CardVerification;
use PaypalServerSdkLib\Models\CardVerificationStatus;
use PaypalServerSdkLib\Models\CheckoutPaymentIntent;
use PaypalServerSdkLib\Models\OAuthToken;
use PaypalServerSdkLib\Models\OrdersCardVerificationMethod;
use PaypalServerSdkLib\Models\PayeePaymentMethodPreference;
use PaypalServerSdkLib\Models\PaypalExperienceLandingPage;
use PaypalServerSdkLib\Models\PaypalExperienceUserAction;
use PaypalServerSdkLib\Models\ShippingPreference;
use PaypalServerSdkLib\PaypalServerSdkClientBuilder;
use think\Exception;

class PaypalAdvanced
{
    protected $client;


    public function __construct()
    {
        $client_id = config('paypal_advanced.client_id');
        $client_secret = config('paypal_advanced.client_secret');

        $this->client = PaypalServerSdkClientBuilder::init()
            ->clientCredentialsAuthCredentials(
                ClientCredentialsAuthCredentialsBuilder::init(
                    $client_id,
                    $client_secret
                )
                    ->oAuthOnTokenUpdate(
                        function (OAuthToken $oAuthToken): void {
                            // Add the callback handler to perform operations like save to DB or file etc.
                            // It will be triggered whenever the token gets updated.
                            $this->saveToken($oAuthToken);
                        }
                    )
                    ->oAuthTokenProvider(
                        function (?OAuthToken $oAuthToken, ClientCredentialsAuthManager $authManager): OAuthToken {
                            // Add the callback handler to provide a new OAuth token.
                            // It will be triggered whenever the lastOAuthToken is null or expired.
                            return $this->loadToken() ?: $authManager->fetchToken();
                        }
                    )
            )
            ->environment(Environment::SANDBOX)
            ->timeout(60)
//            ->proxyConfiguration(ProxyConfigurationBuilder::init('proxy.elecfans.net')->port(3128))
            ->build();
    }

    public function getToken()
    {
        $client_id = config('paypal_advanced.client_id');
        $client_secret = config('paypal_advanced.client_secret');
        $options = [
            'authorization' => 'Basic ' . base64_encode(
                    $client_id . ':' . $client_secret
                ),
            'scope' => null,
        ];
        try {
            $apiResponse = $this->client->getOAuthAuthorizationController()->requestToken($options);
            return $this->handleResponse($apiResponse);
        } catch (\Exception $e) {
            throw new Exception('GET PaypalAdvanced OAuth Token Error: ' . $e->getMessage(), 4001);
        }
    }

    public function createOrder($cart)
    {
        $collect = [
            'body' => OrderRequestBuilder::init(
                CheckoutPaymentIntent::CAPTURE,
                [
                    PurchaseUnitRequestBuilder::init(
                        AmountWithBreakdownBuilder::init("USD", "5")
                            ->breakdown(
                                AmountBreakdownBuilder::init()
                                    ->itemTotal(
                                        MoneyBuilder::init("USD", "5")->build()
                                    )
                                    ->build()
                            )
                            ->build()
                    )
                        ->referenceId('G7471112_log_id')
                        ->customId(2)
                        ->build()
                ]
            )
                ->paymentSource(
                    PaymentSourceBuilder::init()
                        ->paypal(
                            PaypalWalletBuilder::init()
                                ->experienceContext(
                                    PaypalWalletExperienceContextBuilder::init()
                                        ->shippingPreference(ShippingPreference::GET_FROM_FILE)
                                        ->returnUrl('http://tp51.com/pay/capture')
                                        ->cancelUrl('http://tp51.com/pay/cancel')
                                        ->landingPage(PaypalExperienceLandingPage::LOGIN)
                                        ->userAction(PaypalExperienceUserAction::PAY_NOW)
                                        ->paymentMethodPreference(PayeePaymentMethodPreference::IMMEDIATE_PAYMENT_REQUIRED)
                                        ->build()
                                )
                                ->build()
                        )
                        ->card(
                            CardRequestBuilder::init()
                                ->attributes(
                                    CardAttributesBuilder::init()
                                        ->verification(
                                            CardVerificationBuilder::init()
                                                ->method(OrdersCardVerificationMethod::SCA_ALWAYS)
                                                ->build()
                                        )
                                        ->build()
                                )
                                ->build()
                        )
                        ->build()
                )
                ->build(),
        ];

        $apiResponse = $this->client->getOrdersController()->createOrder($collect);
        return $this->handleResponse($apiResponse);
    }

    /**
     * 同步捕获付款
     * @param $orderID
     * @return array
     */
    public function captureOrder($orderID)
    {
        $captureBody = [
            'id' => $orderID
        ];

        $apiResponse = $this->client->getOrdersController()->captureOrder($captureBody);

        return $this->handleResponse($apiResponse);
    }

    /**
     * 异步处理支付回调
     * @param $orderID
     * @return array
     */
    public function notify($orderID)
    {

        $captureBody = [
            'id' => $orderID
        ];

        $apiResponse = $this->client->getPaymentsController()->captureAuthorizedPayment($captureBody);

        return $this->handleResponse($apiResponse);
    }

    protected function saveToken($oAuthToken = null)
    {
        if ($oAuthToken) {
            cache('paypal_auth2_token', $oAuthToken, 3600 - 60);
        } else {
            cache('paypal_auth2_token', null);
        }
    }

    protected function loadToken()
    {
        if (!cache('paypal_auth2_token')) {
            return null;
        }
        return cache('paypal_auth2_token');
    }

    public function handleResponse($response)
    {
        $jsonResponse = json_decode($response->getBody(), true);
        return [
            "jsonResponse" => $jsonResponse,
            "httpStatusCode" => $response->getStatusCode(),
        ];
    }
}